//说明:需要依赖于turf.js库
/**
 * @class LimitHeight
 * @description 限高分析
 * @author YiChen_5177
 * @lastModify  2024.7.4
 */
class LitmitHeight {
  constructor(viewer) {
    this.viewer = viewer;
    this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.drawLayer = new Cesium.CustomDataSource("限高绘制图层");
    this.viewer.dataSources.add(this.drawLayer);
    this.tempLayer = new Cesium.CustomDataSource("限高临时绘制图层");
    this.viewer.dataSources.add(this.tempLayer);
    this.polygon;
    this.polygonPositionArr = [];
    this.minHeight;
    this.maxHeight;
    this.limitHeight;
    this.limitPlane;
    this.basePlane;
    this.limitHeightPrimitive;
    this.label;
    this.labelText;
  }
  //   1.绘制限高区域,得到平面的最低点、最高点.根据最低点得到相对高程为0的平面，
  //   2.最初展示限高过程
  //   3.手动控制限高高度值，来调整限高面
  //获取面数据
  getPolygon() {
    let that = this;
    this.removeEvent();
    return new Promise(function (resolve, reject) {
      let polygon = [];
      let tempPolygon = null;

      that.handler.setInputAction((e) => {
        let pos = that.pxTo3C(e.position);
        if (!Cesium.defined(pos)) return;
        let point = that.createPoint(pos);
        polygon.push(pos);
        that.tempLayer.entities.add(point);
        if (polygon.length == 1) {
          that.handler.setInputAction((evt) => {
            let movePosition = that.pxTo3C(evt.endPosition);
            if (!Cesium.defined(movePosition)) return;
            if (tempPolygon) {
              that.tempLayer.entities.remove(tempPolygon);
              tempPolygon = null;
            }
            tempPolygon = new Cesium.Entity({
              polygon: {
                hierarchy: new Cesium.CallbackProperty(() => {
                  return new Cesium.PolygonHierarchy(
                    polygon.concat(movePosition)
                  );
                }, false),
                material: Cesium.Color.WHITE.withAlpha(0.2),
              },
            });
            that.tempLayer.entities.add(tempPolygon);
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        }
        that.handler.setInputAction(() => {
          that.removeEvent();
          that.tempLayer.entities.removeAll();
          that.polygon = new Cesium.Entity({
            polygon: {
              hierarchy: {
                positions: polygon,
              },
              material: Cesium.Color.WHITE.withAlpha(0),
            },
          });
          that.drawLayer.entities.add(that.polygon);
          that.polygonPositionArr = polygon;
          resolve(polygon);
        }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
      }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
    });
  }

  async startAnalyze() {
    return new Promise(async (resolve, reject) => {
      let that = this;
      let res = await this.getPolygon();
      let heightObj = await this.getMaxMinHeightByGrid(res);
      this.minHeight = heightObj.minHeight;
      this.maxHeight = heightObj.maxHeight;
      this.limitHeight = this.minHeight + 50;
      //绘制限平面
      this.limitPlane = new Cesium.Entity({
        polygon: {
          hierarchy: {
            positions: this.polygonPositionArr,
          },
          height: new Cesium.CallbackProperty(() => {
            return that.limitHeight;
          }, false),
          material: Cesium.Color.WHITE.withAlpha(0.2),
          outline: true,
          outlineWidth: 3,
          outlineColor: Cesium.Color.YELLOW,
        },
      });
      this.drawLayer.entities.add(this.limitPlane);
      this.basePlane = new Cesium.Entity({
        polygon: {
          hierarchy: {
            positions: this.polygonPositionArr,
          },
          height: new Cesium.CallbackProperty(() => {
            return that.minHeight;
          }, false),
          material: Cesium.Color.GREEN.withAlpha(0.5),
          outline: true,
          outlineWidth: 3,
          outlineColor: Cesium.Color.YELLOW,
        },
      });
      this.drawLayer.entities.add(this.basePlane);
      //绘制限高分类平面
      let polygonInstance = new Cesium.GeometryInstance({
        geometry: new Cesium.PolygonGeometry({
          polygonHierarchy: new Cesium.PolygonHierarchy(
            this.polygonPositionArr
          ),
          height: this.limitHeight,
          extrudedHeight: this.limitHeight + 10000,
        }),
        attributes: {
          color: Cesium.ColorGeometryInstanceAttribute.fromColor(
            Cesium.Color.fromCssColorString("#e63f31").withAlpha(0.5)
          ),
          show: new Cesium.ShowGeometryInstanceAttribute(true),
        },
        id: "限高面",
      });
      this.limitHeightPrimitive = this.viewer.scene.primitives.add(
        new Cesium.ClassificationPrimitive({
          geometryInstances: polygonInstance,
          releaseGeometryInstances: false,
          classificationType: Cesium.ClassificationType.BOTH,
        })
      );
      let rectangle = Cesium.Rectangle.fromCartesianArray(
        this.polygonPositionArr
      );
      let centerCartographic = Cesium.Rectangle.center(rectangle);
      this.label = new Cesium.Entity({
        position: new Cesium.CallbackProperty(() => {
          return Cesium.Cartesian3.fromRadians(
            centerCartographic.longitude,
            centerCartographic.latitude,
            this.limitHeight
          );
        }, false),
        label: {
          text: new Cesium.CallbackProperty(() => {
            return `限高:${(this.limitHeight - this.minHeight).toFixed(2)}米`;
          }, false),
          font: "14px sans-serif",
          color: Cesium.Color.BLACK,
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          verticalOrigin: Cesium.VerticalOrigin.CENTER,
          fillColor: Cesium.Color.BLACK,
          outlineColor: Cesium.Color.YELLOW,
          pixelOffset: new Cesium.Cartesian2(0, -15),
          outlineWidth: 5,
          disableDepthTestDistance: 100000,
        },
        point: {
          pixelSize: 5,
          color: Cesium.Color.YELLOW,
          outlineColor: Cesium.Color.AQUA,
          outlineWidth: 3,
        },
      });
      this.drawLayer.entities.add(this.label);
      resolve(heightObj);
    });
  }

  //清除限高分析
  clearLimitHeight() {
    this.removeEvent();
    this.drawLayer.entities.removeAll();
    this.tempLayer.entities.removeAll();
    this.viewer.scene.primitives.remove(this.limitHeightPrimitive);
    this.polygon = null;
    this.polygonPositionArr = [];
    this.minHeight = null;
    this.maxHeight = null;
    this.limitHeight = null;
    this.limitPlane = null;
    this.basePlane = null;

    this.limitHeightPrimitive = null;
    this.label = null;
    this.labelText = null;
  }

  //color 16进制颜色
  //改变限高区域高度和颜色
  setLimitHeight(value, color = "#e63f31") {
    if (!this.limitHeightPrimitive) return;
    if (!this.limitHeightPrimitive.ready) return;
    let cartographic = Cesium.Cartographic.fromCartesian(
      this.limitHeightPrimitive._primitive._boundingSpheres[0].center
    );

    let attribute =
      this.limitHeightPrimitive._primitive.getGeometryInstanceAttributes(
        "限高面"
      );
    attribute.color = Cesium.ColorGeometryInstanceAttribute.toValue(
      Cesium.Color.fromCssColorString(color).withAlpha(0.5)
    );

    let surface = Cesium.Cartesian3.fromRadians(
      cartographic.longitude,
      cartographic.latitude,
      this.minHeight + 50
    );
    let offset = Cesium.Cartesian3.fromRadians(
      cartographic.longitude,
      cartographic.latitude,
      this.minHeight + value
    );
    let translation = Cesium.Cartesian3.subtract(
      offset,
      surface,
      new Cesium.Cartesian3()
    );
    this.limitHeightPrimitive._primitive.modelMatrix =
      Cesium.Matrix4.fromTranslation(translation);
    this.limitHeight = this.minHeight + value;
  }

  //生成网格点并获取极值高程
  getMaxMinHeightByGrid(polygon) {
    return new Promise((resolve, reject) => {
      let rectangle = Cesium.Rectangle.fromCartesianArray(polygon);
      let northwest = Cesium.Rectangle.northwest(
        rectangle,
        new Cesium.Cartographic()
      );
      let southeast = Cesium.Rectangle.southeast(
        rectangle,
        new Cesium.Cartographic()
      );
      let minX = Math.min(
        Cesium.Math.toDegrees(northwest.longitude),
        Cesium.Math.toDegrees(southeast.longitude)
      );
      let maxX = Math.max(
        Cesium.Math.toDegrees(northwest.longitude),
        Cesium.Math.toDegrees(southeast.longitude)
      );
      let minY = Math.min(
        Cesium.Math.toDegrees(northwest.latitude),
        Cesium.Math.toDegrees(southeast.latitude)
      );
      let maxY = Math.max(
        Cesium.Math.toDegrees(northwest.latitude),
        Cesium.Math.toDegrees(southeast.latitude)
      );
      let bbox = [minX, minY, maxX, maxY];
      let polygonGeometry = [];
      polygon.forEach((e) => {
        let carto = Cesium.Cartographic.fromCartesian(e);

        carto.longitude = Cesium.Math.toDegrees(carto.longitude);
        carto.latitude = Cesium.Math.toDegrees(carto.latitude);
        let arr = [carto.longitude, carto.latitude];
        polygonGeometry.push(arr);
      });
      polygonGeometry.push(polygonGeometry[0]);
      let featurePolygon = turf.polygon([polygonGeometry]);
      let cellSide = 0.05;
      let options = {
        mask: featurePolygon,
      };
      let grid = turf.pointGrid(bbox, cellSide, options);
      let coords = turf.coordAll(grid);
      let cartographicArr = [];
      coords.forEach((e) => {
        let carto = {
          longitude: Cesium.Math.toRadians(e[0]),
          latitude: Cesium.Math.toRadians(e[1]),
          height: 0,
        };
        cartographicArr.push(carto);
      });
      let minHeight = 0;
      let maxHeight = 0;
      (async () => {
        // //使用最大级别场景采样
        let promise = new Promise((resolve, reject) => {
          ElMessage.closeAll();
          ElMessage({
            type: "success",
            message: "正在提取限高区域高度信息...",
            duration: 3000,
            offset: 60,
          });
          setTimeout(() => {
            resolve();
          }, 100);
        });
        await promise;
        let cartesianArr = [];
        cartographicArr.forEach((e) => {
          let cartesian = Cesium.Cartesian3.fromRadians(
            e.longitude,
            e.latitude
          );
          cartesianArr.push(cartesian);
        });
        let newCartesianArr = [];
        //非最大级别采样
        cartesianArr.forEach((point) => {
          let newPoint = this.viewer.scene.clampToHeight(point);
          newCartesianArr.push(newPoint);
        });

        newCartesianArr.forEach((item) => {
          let carto = Cesium.Cartographic.fromCartesian(item);
          if (!minHeight) {
            minHeight = carto.height;
          } else {
            minHeight = Math.min(minHeight, carto.height);
          }
          if (!maxHeight) {
            maxHeight = carto.height;
          } else {
            maxHeight = Math.max(maxHeight, carto.height);
          }
        });

        resolve({ minHeight, maxHeight });
      })();
    });
  }

  //笛卡尔转屏幕
  pxTo3C(cartesian2) {
    let depthTest = false;
    depthTest = this.viewer.scene.globe.depthTestAgainstTerrain;
    if (depthTest) {
      return this.viewer.scene.pickPosition(cartesian2);
    } else {
      let ray = this.viewer.camera.getPickRay(cartesian2);
      let cartesian3 = this.viewer.scene.globe.pick(
        ray,
        this.viewer.scene,
        new Cesium.Cartesian3()
      );
      return cartesian3;
    }
  }

  //事件移除器
  removeEvent() {
    if (this.handler) {
      this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
      this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
      this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    }
  }

  //创建点
  createPoint(position) {
    let point = new Cesium.Entity({
      position: position,
      point: {
        pixelSize: 5,
        color: Cesium.Color.fromCssColorString("#3388ff"),
        outline: true,
        outlineWidth: 2,
        outlineColor: Cesium.Color.WHITE,
      },
    });
    return point;
  }
}

export default LitmitHeight;
